package cn.sq.scaffold.ffmpeg.hander;


import cn.sq.scaffold.ffmpeg.config.StatusCode;
import cn.sq.scaffold.ffmpeg.constants.FfmpegConstants;
import cn.sq.scaffold.ffmpeg.exception.BaseExeception;
import cn.sq.scaffold.ffmpeg.exception.FileChangeExeception;
import cn.sq.scaffold.ffmpeg.utils.CmdUtils;
import cn.sq.scaffold.ffmpeg.utils.ListUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * Created with IntelliJ IDEA.
 * User: SQ
 * Date: 2020/11/17
 * Time: 16:04
 * Description:
 * 示例  .\ffmpeg.exe -y -i ..\MVI_2854.MP4  -b:v 400k -s 960x540   demo2.mp4
 */
@Slf4j
@Component
@Data
public class FfmpegHander {

    private String ffmpegFile;        // ffmpeg.exe所放的路径
    private String mencoderFile;        //  mencoder.exe所放的路径
    private String qtFaststartFile;    //  qt-faststart.exe所放的路径

    private String inputFile = "";                // 需转换的原始文件名称
    private String inputFileExtension = "";        // 当前文件的文件后缀

    private String outputFile = "";                // 输出文件名称
    private String outputFileExtension = "mp4";    // 最终转换的文件格式 mp4 flv

    private String imgFile = "";                // 生成缩略图文件名
    private String imgFileExtension = "jpg";    // 生成视频图片截图后缀 jpg gif

    private String width = null;//"800";		// 视频默认宽高
    private String height = null;//"600";		// 视频默认宽高

    private boolean status = false;    // 是否正常状态


    /**
     * ffmpeg能解析转换视频
     *
     * @return boolean
     */
    private boolean processFfmpeg() throws FileChangeExeception {
        List<String> command = new java.util.ArrayList<String>();
        command.add(ffmpegFile);
        command.add("-i");
        command.add(inputFile);
        command.add("-f");
        command.add(outputFileExtension);
        command.add("-c:v");
        command.add("libx264");
        command.add("-b:v");
        command.add("600k");
        command.add("-g");
        command.add("300");
        command.add("-bf");
        command.add("2");
        command.add("-c:a");
        command.add("aac");
        command.add("-strict");
        command.add("experimental");
        command.add("-ac");
        command.add("1");
        command.add("-ar");
        command.add("44100"); // 11025 22050 32000 44100
        command.add("-r");
        command.add("29.97");
        command.add("-qscale");
        command.add("6");
//        if (org.apache.commons.lang3.StringUtils.isNotBlank(width) && org.apache.commons.lang3.StringUtils.isNotBlank(height)){
//            command.add("-s");
//            command.add((width + "x" + height));
//        }
        command.add("-y");
        command.add(outputFile);
        return process(command);
    }


//    /**
//     * 转换视频
//     * @return boolean
//     */
//    public boolean convert() {
//        long startTime = System.currentTimeMillis(); // 获取开始时间
//        boolean statusTemp = status;
//        int type = checkContentType();
//        String tempFile = outputFile + ".tmp";
//        if (statusTemp && type == 0) {
//            log.debug("使用ffmpage进行视频转换");
//            statusTemp = processFfmpeg(inputFile, tempFile);
//        }
////        else if (statusTemp && type == 1) {
////            log.debug("使用mencoder进行视频转换");
////            statusTemp = processMencoder(inputFile, tempFile);
////        }
//        if (statusTemp){
//            log.debug("将mp4视频的元数据信息转到视频第一帧");
//            statusTemp = processQtFaststart(tempFile, outputFile);
//        }
//        log.debug("删除临时文件");
//        FileUtils.deleteFile(tempFile);
//        log.debug("视频转换" + (statusTemp ? "成功" : "失败") + "，用时：" + TimeUtils.formatDateAgo(System.currentTimeMillis() - startTime));
//        return statusTemp;
//    }

    /**
     * 将mp4视频的元数据信息转到视频第一帧
     *
     * @return boolean
     */
    private boolean processQtFaststart(String inputFile, String outputFile) throws FileChangeExeception {
        List<String> command = new ArrayList<String>();
        command.add(qtFaststartFile);
        command.add(inputFile);
        command.add(outputFile);
        return process(command);
    }

    /**
     * 检查文件是否存在
     *
     * @param inputFile
     * @return boolean
     */
    public boolean checkfile(String inputFile) {
        File file = new File(inputFile);
        if (!file.isFile() || !file.exists()) {
            log.warn("文件不存在！");
            return false;
        }
        return true;
    }

    /**
     * 直接转换不需要转成avi在转换
     *
     * @return boolean
     */
    private boolean processMencoder(String inputFile, String outputFile) throws FileChangeExeception {
        List<String> command = new ArrayList<String>();
        command.add(qtFaststartFile);
        command.add(inputFile);
        command.add("-oac");
        command.add("mp3lame");
        command.add("-lameopts");
        command.add("aq=7:vbr=2:q=6");
        command.add("-srate");
        command.add("44100");
        if (org.apache.commons.lang3.StringUtils.isNotBlank(width) && org.apache.commons.lang3.StringUtils.isNotBlank(height)) {
            command.add("-vf");
            command.add(("scale=" + width + ":" + height + ",harddup"));
        }
        command.add("-ovc");
        command.add("xvid");
        command.add("-xvidencopts");
        command.add("fixed_quant=8");
        command.add("-of");
        command.add("lavf");
        command.add("-o");
        command.add(outputFile);
        return process(command);
    }


    /**
     * 执行命令
     *
     * @param command
     * @return boolean
     */
    public boolean process(List<String> command) throws FileChangeExeception {
        try {
            log.debug(ListUtils.convertToString(command, " "));
//			Process process = new ProcessBuilder(command).redirectErrorStream(true).start();
            Process process = Runtime.getRuntime().exec(command.toArray(new String[command.size()]));
            new PrintErrorReader(process.getErrorStream()).start();
            new PrintInputStream(process.getInputStream()).start();
            process.waitFor();
            return true;
        } catch (Exception e) {
            if (org.apache.commons.lang3.StringUtils.contains(e.getMessage(), "CreateProcess error=2")) {
                log.error("缺少视频转换工具，请配置video.ffmpegFile相关参数。" + e.getMessage());
            } else {
                log.error(e.getMessage(), e);
            }
            throw new FileChangeExeception("转化失败");
        }
    }

    /**
     * ffmpeg 截取缩略图
     *
     * @param inputFile
     * @return boolean
     */
    public boolean processFfmpegCutpic(String inputFile, String outputFile) throws FileChangeExeception {
        List<String> command = new java.util.ArrayList<String>();
        command.add(ffmpegFile);
        command.add("-i");
        command.add(inputFile);
        if ((imgFileExtension.toLowerCase()).equals("gif")) {
            command.add("-vframes");
            command.add("30");
            command.add("-f");
            command.add("gif");
        } else {
            command.add("-ss");
            command.add("4");
            command.add("-t");
            command.add("0.001");
            command.add("-f");
            command.add("image2");
        }
//		if (StringUtils.isNotBlank(width) && StringUtils.isNotBlank(height)){
//			command.add("-s");
//			command.add((width + "x" + height));
//		}
        command.add("-y");
        command.add(imgFile);
        return process(command);

    }


    /**
     * 截取图片
     *
     * @return boolean
     */
//    public boolean cutPic() {
//        long startTime = System.currentTimeMillis(); // 获取开始时间
//        boolean statusTemp = status;
//        if (statusTemp) {
//            statusTemp = processFfmpegCutpic(inputFile, outputFile);
//            try {
//                File imgfile = new File(imgFile);
//                if (imgfile.exists()){
//                    ImageUtils.thumbnails(imgfile, 800, 600, null);
//                }else{
//                    statusTemp = false;
//                }
//            } catch (Exception e) {
//                statusTemp = false;
//                log.error("视频剪切图片失败", e);
//            }
//        }
//        log.debug("视频剪切图片" + (statusTemp ? "成功" : "失败") + "，用时：" + TimeUtils.formatDateAgo(System.currentTimeMillis() - startTime));
//        return statusTemp;
//    }


    class PrintInputStream extends Thread {
        java.io.InputStream __is = null;

        public PrintInputStream(java.io.InputStream is) {
            __is = is;
        }

        @Override
        public void run() {
            try {
                BufferedReader br = new BufferedReader(new InputStreamReader(__is));
                String line = null;
                while ((line = br.readLine()) != null) {
                    log.debug(line);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    class PrintErrorReader extends Thread {
        java.io.InputStream __is = null;

        public PrintErrorReader(java.io.InputStream is) {
            __is = is;
        }

        @Override
        public void run() {
            try {
                BufferedReader br = new BufferedReader(new InputStreamReader(__is));
                String line = null;
                while ((line = br.readLine()) != null) {
                    log.info(line);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     *
     * @param inputStream 文件流
     * @param originalFilename 文件名称
     * @return
     * @throws BaseExeception
     * @throws FileChangeExeception
     * @throws IOException
     */
    public boolean asyncprocess(InputStream inputStream,String originalFilename) throws BaseExeception, FileChangeExeception, IOException {
        if (!CmdUtils.checkContentType(CmdUtils.getFileSuffix(originalFilename))) {
            throw new BaseExeception("文件格式不正确", 100);
        }
        //新的文件名称
        String output_file_name = UUID.randomUUID() + ".mp4";
        //保存以前的文件
        String oldFilepath = FfmpegConstants.INPUTFILEPATH + UUID.randomUUID()+CmdUtils.getFileSuffix(originalFilename);
//        boolean b = CmdUtils.multipartFileToFile(file, oldFilepath);
        FileOutputStream fileOutputStream = new FileOutputStream(oldFilepath);

        int copy = IOUtils.copy(inputStream, fileOutputStream);

        String newFilepath = FfmpegConstants.OUTPUTFILEPATH + output_file_name;
        ArrayList<String> strings = new ArrayList<>();
        strings.add(FfmpegConstants.FFMPEGPATH);
        strings.add("-i");
        strings.add(oldFilepath);
        strings.add("-threads");
        strings.add("5");
        strings.add("-preset");
        strings.add("ultrafast");
        strings.add("-b:v");
        strings.add("1024k");
        strings.add(newFilepath);
        Thread t = new Thread(() -> {
            try {
                process(strings);
                new File(oldFilepath).delete();
            } catch (FileChangeExeception fileChangeExeception) {
                fileChangeExeception.printStackTrace();
            }
        });
        t.start();
        return true;
    }

//    /**
//     * 异步转码 根据指定的uuid生成
//     * @param inputStream 文件流
//     * @param originalFilename 文件名称
//     * @param uuid 自定义uuid
//     * @return
//     * @throws BaseExeception
//     * @throws FileChangeExeception
//     * @throws IOException
//     */
//    public boolean asyncprocess(InputStream inputStream,String originalFilename,String uuid) throws BaseExeception, FileChangeExeception, IOException {
//        if (!CmdUtils.checkContentType(CmdUtils.getFileSuffix(originalFilename))) {
//            throw new BaseExeception("文件格式不正确", 100);
//        }
//        //新的文件名称
//        String output_file_name = uuid + ".mp4";
//        //保存以前的文件
//        String oldFilepath = FfmpegConstants.INPUTFILEPATH + UUID.randomUUID()+CmdUtils.getFileSuffix(originalFilename);
////        boolean b = CmdUtils.multipartFileToFile(file, oldFilepath);
//        FileOutputStream fileOutputStream = new FileOutputStream(oldFilepath);
//
//        int copy = IOUtils.copy(inputStream, fileOutputStream);
//
//        String newFilepath = FfmpegConstants.OUTPUTFILEPATH + output_file_name;
//        ArrayList<String> strings = new ArrayList<>();
//        strings.add(FfmpegConstants.FFMPEGPATH);
//        strings.add("-i");
//        strings.add(oldFilepath);
//        strings.add("-threads");
//        strings.add("5");
//        strings.add("-preset");
//        strings.add("ultrafast");
//        strings.add("-b:v");
//        strings.add("1024k");
//        strings.add(newFilepath);
//        Thread t = new Thread(() -> {
//            try {
//                process(strings);
//                new File(oldFilepath).delete();
//            } catch (FileChangeExeception fileChangeExeception) {
//                fileChangeExeception.printStackTrace();
//            }
//        });
//        t.start();
//        return true;
//    }

    /**
     * 异步转码 根据指定的文件路径生成生成
     * @param inputStream
     * @param originalFilename
     * @param newFilepath
     * @return
     * @throws BaseExeception
     * @throws FileChangeExeception
     * @throws IOException
     */
    public boolean asyncprocess(InputStream inputStream,String originalFilename,String newFilepath) throws BaseExeception, FileChangeExeception, IOException {
        if (!CmdUtils.checkContentType(CmdUtils.getFileSuffix(originalFilename))) {
            throw new BaseExeception("文件格式不正确", 100);
        }
//        //新的文件名称
//        String output_file_name = uuid + ".mp4";
        //保存以前的文件
        String oldFilepath = FfmpegConstants.INPUTFILEPATH + UUID.randomUUID()+CmdUtils.getFileSuffix(originalFilename);
//        boolean b = CmdUtils.multipartFileToFile(file, oldFilepath);
        FileOutputStream fileOutputStream = new FileOutputStream(oldFilepath);

        int copy = IOUtils.copy(inputStream, fileOutputStream);

//        String newFilepath = FfmpegConstants.OUTPUTFILEPATH + output_file_name;
        ArrayList<String> strings = new ArrayList<>();
        strings.add(FfmpegConstants.FFMPEGPATH);
        strings.add("-i");
        strings.add(oldFilepath);
        strings.add("-threads");
        strings.add("5");
        strings.add("-preset");
        strings.add("ultrafast");
        strings.add("-b:v");
        strings.add("1024k");
        strings.add(newFilepath);
        Thread t = new Thread(() -> {
            try {
                process(strings);
                new File(oldFilepath).delete();
            } catch (FileChangeExeception fileChangeExeception) {
                fileChangeExeception.printStackTrace();
            }
        });
        t.start();
        return true;
    }


    /**
     * 同步转换
     * @param inputStream
     * @param originalFilename
     * @return
     * @throws BaseExeception
     * @throws FileChangeExeception
     * @throws IOException
     */
    public String syncprocess(InputStream inputStream,String originalFilename) throws BaseExeception, FileChangeExeception, IOException {
        log.debug(originalFilename);
        if (!CmdUtils.checkContentType(CmdUtils.getFileSuffix(originalFilename))) {
            throw new BaseExeception("文件格式不正确", 100);
        }
        //新的文件名称
        String output_file_name = UUID.randomUUID() + ".mp4";
        //保存以前的文件
        String oldFilepath = FfmpegConstants.INPUTFILEPATH + UUID.randomUUID()+CmdUtils.getFileSuffix(originalFilename);

        String newFilepath = FfmpegConstants.OUTPUTFILEPATH + output_file_name;
//        FileUtils.cleanDirectory(new File(newFilepath));
//        FileUtils.cleanDirectory(new File(oldFilepath));
        FileOutputStream fileOutputStream = new FileOutputStream(oldFilepath);
//        boolean b = CmdUtils.multipartFileToFile(, oldFilepath);

        int copy = IOUtils.copy(inputStream, fileOutputStream);
//        File file1 = new File(oldFilepath);
//        file1.mkdir();
//        file.transferTo(file1);

        ArrayList<String> strings = new ArrayList<>();
        strings.add(FfmpegConstants.FFMPEGPATH);
        strings.add("-i");
        strings.add(oldFilepath);
        strings.add("-threads");
        strings.add("5");
        strings.add("-preset");
        strings.add("ultrafast");
        strings.add("-b:v");
        strings.add("1024k");
        strings.add(newFilepath);
        boolean process = process(strings);
        new File(oldFilepath).delete();
        if (process) {
            return output_file_name;
        }
        throw new BaseExeception(StatusCode.Error);

    }
}
